package calculator;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.StringTokenizer;

public class Calculator
{
	private Stack			stack;
	// Regulaerer Ausdruck fuer binaere operatorn
	private final String	binaryOperatorsRegExpression			= "max|min";

	private final String	binaryOperatorsRegExpressionEinZeichen	= "%|/|-|\\+|\\*|\\^";
	// Regulaerer Ausdruck fuer unaere operatorn
	private final String	unaryOperatorsRegExpression				= "1/x|sqrt|ceil|floor|sin|cos|abs|log";

	// Regulaerer Ausdruck fuer zahlen
	private final String	numbersRegExpression					= "-?[0-9]+(\\.[0-9]+)?|Infinity|NaN";

	public Calculator()
	{
		this.stack = new Stack();
	}

	public static void main(String[] args) throws IOException
	{
		Calculator calculator = new Calculator();

		String upn_expr = "8 6 3 / /";
		System.out.println(calculator.getSimpleUPN(upn_expr));
		System.out.println(calculator.getRestExeptSimpleUPN(upn_expr));

		double erg = calculator.calculate_upn(upn_expr);
		System.out.println("Das war doch simpel.. :) [" + upn_expr + "] = " + erg);

	}

	public String getSimpleUPN(String upn)
	{
		String[] words = upn.split(" ");
		int lastIndex = words.length - 1;
		String operator = words[lastIndex];
		boolean binary = this.isBinaryOperator(operator)
				|| this.isBinaryOperatorEinZeichen(operator);

		String arg1 = "", arg2 = "";
		if (binary)
		{
			arg1 = words[lastIndex - 1];
			arg2 = words[lastIndex - 2];
		}
		
		else
		{
			arg1 = words[lastIndex - 1];
		}

		return (arg2 + " " + arg1 + " " + operator).trim();
	}

	public String getRestExeptSimpleUPN(String upn)
	{
		String strToReplace = this.getSimpleUPN(upn);
		String result = upn.replace(strToReplace, "").trim();
		return result;
	}

	private boolean isNumber(String word)
	{
		return word.matches(this.numbersRegExpression); // muster fuer zahlen check
	}

	private boolean isBinaryOperator(String word)
	{
		return word.matches(this.binaryOperatorsRegExpression); // muster fuer zahlen check
	}

	private boolean isBinaryOperatorEinZeichen(String word)
	{
		return word.matches(this.binaryOperatorsRegExpressionEinZeichen); // muster fuer zahlen
		// check
	}

	private boolean isUnaryOperator(String word)
	{
		return word.matches(this.unaryOperatorsRegExpression);
	}

	public double calculate_upn(String upn_expr)
	{
		StringTokenizer tok = new StringTokenizer(upn_expr, " ");

		while (tok.hasMoreTokens())
		{
			String nextWord = tok.nextToken();

			if (isNumber(nextWord))
			{

				stack.push(Double.parseDouble(nextWord));

			} else if (isBinaryOperatorEinZeichen(nextWord))
			{

				Double elem1 = stack.pop();
				Double elem2 = stack.pop();
				double result = 0;

				switch (nextWord.charAt(0))
				{

					case '%':
						result = elem2 % elem1;
						break;
					case '+':
						result = elem1 + elem2;
						break;
					case '-':
						result = elem2 - elem1;
						break;
					case '*':
						result = elem1 * elem2;
						break;
					case '/':
						result = elem2 / elem1;
						break;
					case '^':
						result = Math.pow(elem2, elem1);
						break;

				}
				stack.push(result);
			} else if (isBinaryOperator(nextWord))
			{

				Double elem1 = stack.pop();
				Double elem2 = stack.pop();
				double result = 0;

				if (nextWord.equals("max"))
				{
					result = Math.max(elem1, elem2);
				}

				else if (nextWord.equals("min"))
				{
					result = Math.min(elem1, elem2);
				}

				stack.push(result);

			} else if (isUnaryOperator(nextWord))
			{
				Double elem1 = stack.pop();

				double result = 0;
				
				if (nextWord.equals("1/x"))
				{
					result = 1 / elem1;

				}
				if (nextWord.equals("sqrt"))
				{
					result = Math.sqrt(elem1);

				}
				if (nextWord.equals("ceil"))
				{
					result = Math.ceil(elem1);

				}

				if (nextWord.equals("floor"))
				{
					result = Math.floor(elem1);

				}

				if (nextWord.equals("sin"))
				{
					result = Math.sin(elem1);

				}
				if (nextWord.equals("cos"))
				{
					result = Math.cos(elem1);

				}
				if (nextWord.equals("abs"))
				{
					result = Math.abs(elem1);

				}

				if (nextWord.equals("log"))
				{
					result = Math.log(elem1);

				}

				stack.push(result);
			}
		}
		return stack.pop();
	}

	@SuppressWarnings("unused")
	private String readIn() throws IOException
	{
		BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
		String read = br.readLine();
		return read;
	}

}